package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tianji.api.client.user.UserClient;
import com.tianji.api.dto.user.UserDTO;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constans.RedisConstans;
import com.tianji.learning.domian.query.PointsBoardQuery;
import com.tianji.learning.domian.vo.PointsBoardItemVO;
import com.tianji.learning.domian.vo.PointsBoardVO;
import com.tianji.learning.entity.PointsBoard;
import com.tianji.learning.mapper.PointsBoardMapper;
import com.tianji.learning.service.IPointsBoardService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.TableInfoContext;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.tianji.common.utils.DateUtils.BOARDS_DATE_SUFFIX_FORMATTER;
import static com.tianji.learning.config.LearningConstants.POINTS_BOARD_TABLE_PREFIX;

/**
 * <p>
 * 学霸天梯榜 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-03-25
 */
@Service
@RequiredArgsConstructor
public class PointsBoardServiceImpl extends ServiceImpl<PointsBoardMapper, PointsBoard> implements IPointsBoardService {

    private final StringRedisTemplate redisTemplate;

    private final UserClient userClient;


    @Override
    public PointsBoardVO pagequeryPointsBoardbyseasons(PointsBoardQuery query) {
        //1、判断是否是查询当前赛季
        Long season = query.getSeason();
        boolean isCurrent = ObjectUtil.isEmpty(season) || season == 0;

        //2、查询我的积分信息
        LocalDateTime now = LocalDateTime.now();
        String key = RedisConstans.POINTS_BOARD_KEY_PREFIX + now.format(BOARDS_DATE_SUFFIX_FORMATTER);
        PointsBoard myBoard = isCurrent ? queryMyCurrentBoard(key) : queryMyHistoryBoard(season);

        //3、查询当前赛季的榜单列表
        List<PointsBoard> list = isCurrent ? queryCurrentBoardList(key, query.getPageNo(), query.getPageSize()) : queryHistoryBoardList(query);

        //4、封装VO
        PointsBoardVO vo = new PointsBoardVO();
        //4.1、处理我的数据
        if (ObjectUtil.isNotEmpty(myBoard)) {
            vo.setPoints(myBoard.getPoints());
            vo.setRank(myBoard.getRank());
        }

        //4.2、处理榜单数据
        if (ObjectUtil.isEmpty(list)) {
            return vo;
        }
        //4.2.1、查询用户信息
        Set<Long> userIds = list.stream().map(PointsBoard::getUserId).collect(Collectors.toSet());
        List<UserDTO> userInfoList = userClient.queryUserByIds(userIds);
        Map<Long, String> userNameMap = new HashMap<>(userIds.size());
        if (ObjectUtil.isNotEmpty(userInfoList)) {
            userNameMap = userInfoList.stream().collect(Collectors.toMap(UserDTO::getId, UserDTO::getName));
        }

        //4.2.2、循环组装ItemVo List
        List<PointsBoardItemVO> boardList = new ArrayList<>(list.size());
        for (PointsBoard board : list) {
            PointsBoardItemVO itemVO = new PointsBoardItemVO();
            itemVO.setPoints(board.getPoints());
            itemVO.setRank(board.getRank());
            itemVO.setName(userNameMap.getOrDefault(board.getUserId(), ""));
            boardList.add(itemVO);
        }
        vo.setBoardList(boardList);
        //5、返回Vo
        return vo;
    }



    public List<PointsBoard> queryCurrentBoardList(String key, Integer pageNo, Integer pageSize) {
        //1、查询 0-9      10-19
        int from = (pageNo - 1) * pageSize;
        int end = from + pageSize - 1;
        Set<ZSetOperations.TypedTuple<String>> tuples =
                redisTemplate.opsForZSet().reverseRangeWithScores(key, from, end);
        if (ObjectUtil.isEmpty(tuples)) {
            return CollUtils.emptyList();
        }

        //2、封装Lis返回
        int rank = from + 1;
        List<PointsBoard> list = new ArrayList<>(tuples.size());
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            String userId = tuple.getValue();
            Double points = tuple.getScore();
            if (ObjectUtil.isEmpty(userId) || ObjectUtil.isEmpty(points)) {
                continue;
            }
            PointsBoard board = new PointsBoard();
            board.setUserId(Long.valueOf(userId));
            board.setPoints(points.intValue());
            board.setRank(rank++);
            list.add(board);
        }
        return list;
    }

    private PointsBoard queryMyHistoryBoard(Long season) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.计算表名
        TableInfoContext.setInfo(POINTS_BOARD_TABLE_PREFIX + season);
        // 3.查询数据
        Optional<PointsBoard> opt = lambdaQuery().eq(PointsBoard::getUserId, userId).oneOpt();
        if (opt.isEmpty()) {
            return null;
        }
        // 4.转换数据
        PointsBoard pointsBoard = opt.get();
        pointsBoard.setRank(pointsBoard.getId().intValue());
        return pointsBoard;
    }

    private List<PointsBoard> queryHistoryBoardList(PointsBoardQuery query) {
        // 1.计算表名
        TableInfoContext.setInfo(POINTS_BOARD_TABLE_PREFIX + query.getSeason());
        // 2.查询数据
        Page<PointsBoard> page = page(query.toMpPage());
        // 3.数据处理
        List<PointsBoard> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return CollUtils.emptyList();
        }
        records.forEach(b -> b.setRank(b.getId().intValue()));
        return records;
    }

    private PointsBoard queryMyCurrentBoard(String key) {
        //1.绑定key
        BoundZSetOperations<String, String> opsBound = redisTemplate.boundZSetOps(key);

        //2.获取当前登录人
        String userId = UserContext.getUser().toString();

        //3.查询积分
        Double points = opsBound.score(userId);

        //4.查询排名
        Long rank = opsBound.reverseRank(userId);

        //5.封装返回
        PointsBoard board = new PointsBoard();
        board.setPoints(points == null ? 0 : points.intValue());
        board.setRank(rank == null ? 0 : rank.intValue() + 1);
        return board;

    }


    @Override
    public void createPointsBoardTableBySeason(Integer season) {
        getBaseMapper().createPointsBoardTable(POINTS_BOARD_TABLE_PREFIX + season);
    }
}
